﻿namespace TwitterApi
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.IO;
    using System.Web;
    using System.Web.Helpers;
    using System.Xml;
    using System.Xml.Serialization;
    using Hammock;
    using Hammock.Authentication.OAuth;

    public static class Proxy
    {
        public const string TwitterApiUrl = "http://api.twitter.com/";

        public const string UserParametersNotSpecifiedMessage = "Either the user Id or screen name should be specified.";
        public const string ApiErrorMessage = "Twitter API Error: {0}";

        public static OAuthToken GetRequestToken(string consumerKey, string consumerSecret, string callbackUrl = "")
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.RequestToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                CallbackUrl = callbackUrl
            };
 
            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials
            };

            RestRequest request = new RestRequest
            {
                Path = "oauth/request_token"
            };

            RestResponse response = client.Request(request);

            NameValueCollection collection = HttpUtility.ParseQueryString(response.Content);

            if (collection["oauth_token"] == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ApiErrorMessage, response.Content));
            }

            return new OAuthToken
            {
                Token = collection["oauth_token"],
                Secret = collection["oauth_token_secret"]
            };
        }

        public static OAuthToken GetAccessToken(string consumerKey, string consumerSecret, OAuthToken requestToken, string requestVerifier)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = requestToken.Token,
                TokenSecret = requestToken.Secret,
                Verifier = requestVerifier
            };

            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials
            };

            RestRequest request = new RestRequest
            {
                Path = "oauth/access_token"
            };

            RestResponse response = client.Request(request);

            NameValueCollection collection = HttpUtility.ParseQueryString(response.Content);

            if (collection["oauth_token"] == null)
            {
                XmlSerializer serializer = new XmlSerializer(typeof(AccessTokenError));
                var accesstokenError = serializer.Deserialize(new StringReader(response.Content)) as AccessTokenError;
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ApiErrorMessage, accesstokenError.Error));
            }

            return new OAuthToken
            {
                Token = collection["oauth_token"],
                Secret = collection["oauth_token_secret"]
            };
        }

        public static IList<dynamic> GetPublicTimeline(bool trimUser = false, bool includeEntities = false)
        {
            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                VersionPath = "1"
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/public_timeline.json"
            };
            
            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());

            RestResponse response = client.Request(request);

            var result = Json.Decode(response.Content);

            CheckForErrors(result);

            return new List<dynamic>(result);
        }

        public static IList<dynamic> GetHomeTimeline(
                                            string consumerKey,
                                            string consumerSecret,
                                            OAuthToken accessToken,
                                            long sinceId = 0,
                                            long maxId = 0,
                                            int count = 0,
                                            int page = 0,
                                            bool trimUser = false,
                                            bool includeEntities = false)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = accessToken.Token,
                TokenSecret = accessToken.Secret,
            };

            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/home_timeline.json"
            };

            if (sinceId != 0)
            {
                request.AddParameter("since_id", sinceId.ToString());
            }

            if (maxId != 0)
            {
                request.AddParameter("max_id", maxId.ToString());
            }

            if (count != 0)
            {
                request.AddParameter("count", count.ToString());
            }

            if (page != 0)
            {
                request.AddParameter("page", page.ToString());
            }

            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());

            RestResponse response = client.Request(request);

            var result = Json.Decode(response.Content);

            CheckForErrors(result);

            return new List<dynamic>(result);
        }

        public static IList<dynamic> GetFriendsTimeline(
                                            string consumerKey,
                                            string consumerSecret,
                                            OAuthToken accessToken,
                                            long sinceId = 0,
                                            long maxId = 0,
                                            int count = 0,
                                            int page = 0,
                                            bool trimUser = false,
                                            bool includeRetweets = false,
                                            bool includeEntities = false)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = accessToken.Token,
                TokenSecret = accessToken.Secret,
            };

            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/friends_timeline.json"
            };

            if (sinceId != 0)
            {
                request.AddParameter("since_id", sinceId.ToString());
            }

            if (maxId != 0)
            {
                request.AddParameter("max_id", maxId.ToString());
            }

            if (count != 0)
            {
                request.AddParameter("count", count.ToString());
            }

            if (page != 0)
            {
                request.AddParameter("page", page.ToString());
            }

            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_rts", includeRetweets.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());

            RestResponse response = client.Request(request);

            var result = Json.Decode(response.Content);

            CheckForErrors(result);

            return new List<dynamic>(result);
        }

        public static IList<dynamic> GetUserTimeline(
                                            string consumerKey,
                                            string consumerSecret,
                                            OAuthToken accessToken,
                                            long userId = 0,
                                            string screenName = "",
                                            long sinceId = 0,
                                            long maxId = 0,
                                            int count = 0,
                                            int page = 0,
                                            bool trimUser = false,
                                            bool includeRetweets = false,
                                            bool includeEntities = false)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = accessToken.Token,
                TokenSecret = accessToken.Secret,
            };

            return GetUserTimeline(credentials, userId, screenName, sinceId, maxId, count, page, trimUser, includeRetweets, includeEntities);
        }

        public static IList<dynamic> GetUserTimeline(
                                            long userId = 0,
                                            string screenName = "",
                                            long sinceId = 0,
                                            long maxId = 0,
                                            int count = 0,
                                            int page = 0,
                                            bool trimUser = false,
                                            bool includeRetweets = false,
                                            bool includeEntities = false)
        {
            if (userId == 0 && string.IsNullOrEmpty(screenName))
            {
                throw new ArgumentException(UserParametersNotSpecifiedMessage);
            }

            return GetUserTimeline(null, userId, screenName, sinceId, maxId, count, page, trimUser, includeRetweets, includeEntities);
        }

        public static IList<dynamic> GetMentions(
                                            string consumerKey, 
                                            string consumerSecret, 
                                            OAuthToken accessToken,
                                            long sinceId = 0, 
                                            long maxId = 0, 
                                            int count = 0, 
                                            int page = 0,
                                            bool trimUser = false, 
                                            bool includeRetweets = false,
                                            bool includeEntities = false)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = accessToken.Token,
                TokenSecret = accessToken.Secret,
            };

            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials,
                VersionPath = "1"
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/mentions.json"
            };

            if (sinceId != 0)
            {
                request.AddParameter("since_id", sinceId.ToString());
            }

            if (maxId != 0)
            {
                request.AddParameter("max_id", maxId.ToString());
            }

            if (count != 0)
            {
                request.AddParameter("count", count.ToString());
            }

            if (page != 0)
            {
                request.AddParameter("page", page.ToString());
            }

            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_rts", includeRetweets.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());            

            RestResponse response = client.Request(request);

            var result = Json.Decode(response.Content);

            CheckForErrors(result);

            return new List<dynamic>(result);
        }

        public static IList<dynamic> GetRetweetedByMe(
                                           string consumerKey,
                                           string consumerSecret,
                                           OAuthToken accessToken,                                                                                      
                                           long sinceId = 0,
                                           long maxId = 0,
                                           int count = 0,
                                           int page = 0,
                                           bool trimUser = false,                                           
                                           bool includeEntities = false)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = accessToken.Token,
                TokenSecret = accessToken.Secret,
            };

            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials,
                VersionPath = "1"
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/retweeted_by_me.json"
            };

            if (sinceId != 0)
            {
                request.AddParameter("since_id", sinceId.ToString());
            }

            if (maxId != 0)
            {
                request.AddParameter("max_id", maxId.ToString());
            }

            if (count != 0)
            {
                request.AddParameter("count", count.ToString());
            }

            if (page != 0)
            {
                request.AddParameter("page", page.ToString());
            }

            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());            

            RestResponse response = client.Request(request);

            var retweeted = Json.Decode(response.Content);

            return new List<dynamic>(retweeted);
        }

        public static IList<dynamic> GetRetweetedToMe(
                                     string consumerKey,
                                     string consumerSecret,
                                     OAuthToken accessToken,
                                     long sinceId = 0,
                                     long maxId = 0,
                                     int count = 0,
                                     int page = 0,
                                     bool trimUser = false,
                                     bool includeEntities = false)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = accessToken.Token,
                TokenSecret = accessToken.Secret,
            };

            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials,
                VersionPath = "1"
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/retweeted_to_me.json"
            };

            if (sinceId != 0)
            {
                request.AddParameter("since_id", sinceId.ToString());
            }

            if (maxId != 0)
            {
                request.AddParameter("max_id", maxId.ToString());
            }

            if (count != 0)
            {
                request.AddParameter("count", count.ToString());
            }

            if (page != 0)
            {
                request.AddParameter("page", page.ToString());
            }

            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());

            RestResponse response = client.Request(request);

            var retweeted = Json.Decode(response.Content);

            return new List<dynamic>(retweeted);
        }

        public static IList<dynamic> GetRetweetsOfMe(
                             string consumerKey,
                             string consumerSecret,
                             OAuthToken accessToken,
                             long sinceId = 0,
                             long maxId = 0,
                             int count = 0,
                             int page = 0,
                             bool trimUser = false,
                             bool includeEntities = false)
        {
            OAuthCredentials credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                Token = accessToken.Token,
                TokenSecret = accessToken.Secret,
            };

            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials,
                VersionPath = "1"
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/retweets_of_me.json"
            };

            if (sinceId != 0)
            {
                request.AddParameter("since_id", sinceId.ToString());
            }

            if (maxId != 0)
            {
                request.AddParameter("max_id", maxId.ToString());
            }

            if (count != 0)
            {
                request.AddParameter("count", count.ToString());
            }

            if (page != 0)
            {
                request.AddParameter("page", page.ToString());
            }

            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());

            RestResponse response = client.Request(request);

            var retweets = Json.Decode(response.Content);

            return new List<dynamic>(retweets);
        }

        private static IList<dynamic> GetUserTimeline(
                                            OAuthCredentials credentials,
                                            long userId = 0,
                                            string screenName = "",
                                            long sinceId = 0,
                                            long maxId = 0,
                                            int count = 0,
                                            int page = 0,
                                            bool trimUser = false,
                                            bool includeRetweets = false,
                                            bool includeEntities = false)
        {
            RestClient client = new RestClient
            {
                Authority = TwitterApiUrl,
                Credentials = credentials,
                VersionPath = "1"
            };

            RestRequest request = new RestRequest
            {
                Path = "statuses/user_timeline.json"
            };

            if (userId != 0)
            {
                request.AddParameter("user_id", userId.ToString());
            }

            if (!string.IsNullOrEmpty(screenName))
            {
                request.AddParameter("screen_name", screenName.ToString());
            }

            if (sinceId != 0)
            {
                request.AddParameter("since_id", sinceId.ToString());
            }

            if (maxId != 0)
            {
                request.AddParameter("max_id", maxId.ToString());
            }

            if (count != 0)
            {
                request.AddParameter("count", count.ToString());
            }

            if (page != 0)
            {
                request.AddParameter("page", page.ToString());
            }

            request.AddParameter("trim_user", trimUser.ToString().ToLowerInvariant());
            request.AddParameter("include_entities", includeEntities.ToString().ToLowerInvariant());
            request.AddParameter("include_rts", includeRetweets.ToString().ToLowerInvariant());

            RestResponse response = client.Request(request);

            var result = Json.Decode(response.Content);

            CheckForErrors(result);

            return new List<dynamic>(result);
        }

        private static void CheckForErrors(dynamic result)
        {
            if (result.error != null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ApiErrorMessage, result.error));
            }
        }
    }
}